package image;

import static math.math.*;
import math.vec2;
import math.vec2i;

public class Image1 extends Image
{
    
    private double[] mData;
    
    
    public Image1(vec2i aSize)
    {
        // Init the Image size:
        super(aSize);
        // Init the array of data:
        int numCells = aSize.x * aSize.y;
        this.mData = new double[numCells];
    }
    
    
    public Image copy(
            vec2i aStart, 
            vec2i aSize)
    {
        Image1 copy = new Image1(aSize);
        Image1.copy(this, aStart, copy, vec2i.ZERO, aSize);
        return copy;
    }
    
    
    /** Writes a datum to this Image. */
    public void write(
            vec2i aPixel, 
            double aDatum)
    {
        this.write(this.toIntIndex(aPixel), aDatum);
    }
    
    /** Writes a datum to this Image. */
    public void write(
            int aIndex, 
            double aDatum)
    {
        this.mData[aIndex] = aDatum;
    }
    
    
    /** Returns a datum from this Image. */
    public double read(vec2i aPixel)
    {
        return this.read(this.toIntIndex(aPixel));
    }
    
    /** Returns a datum from this Image. */
    public double read(int aIndex)
    {
        return this.mData[aIndex];
    }
    
    
    public double lookupNN(vec2 uv)
    {
        final vec2 imgSize = new vec2(this.mSize);
        imgSize.sub(1.0);
        final vec2 st = vec2.mul(uv, imgSize);
        st.add(0.5);
        vec2i texel = new vec2i(st);
        return this.read(texel);
    }
    
    
    public static void writeAll(
            Image1 aImage, 
            double aDatum)
    {
        for (int i = 0; i < aImage.mData.length; i++)
        {
            aImage.mData[i] = aDatum;
        }
    }
    
    public static void scaleAll(
            Image1 aImage, 
            double aScale)
    {
        for (int i = 0; i < aImage.mData.length; i++)
        {
            aImage.mData[i] *= aScale;
        }
    }
    
    public static void clampAll(
            Image1 aImage, 
            double aBoundsLo, 
            double aBoundsHi)
    {
        for (int i = 0; i < aImage.mData.length; i++)
        {
            double clamped = clampd(aImage.mData[i], aBoundsLo, aBoundsHi);
            aImage.mData[i] = clamped;
        }
    }
    
    public static void powAll(
            Image1 aImage, 
            double aExp)
    {
        for (int i = 0; i < aImage.mData.length; i++)
        {
            double pow = powd(aImage.mData[i], aExp);
            aImage.mData[i] = pow;
        }
    }
    
    public static void mapAll(
            Image1 aImage, 
            double aBoundsLo, 
            double aBoundsHi)
    {
        vec2 minmax = new vec2();
        for (int i = 0; i < aImage.mData.length; i++)
        {
            double datum = aImage.mData[i];
            if (datum < minmax.x)
            {
                minmax.x = datum;
            }
            if (datum > minmax.y)
            {
                minmax.y = datum;
            }
        }
        double rwidth = 1.0 / (minmax.y - minmax.x);
        double newWidth = aBoundsHi - aBoundsLo;
        for (int i = 0; i < aImage.mData.length; i++)
        {
            double norm = (aImage.mData[i] - minmax.x) * rwidth;
            double mapped = aBoundsLo + (norm * newWidth);
            aImage.mData[i] = mapped;
        }
    }
    
    public static void copy(
            Image1 aSRC, 
            vec2i aSRCStart, 
            Image1 aDST, 
            vec2i aDSTStart, 
            vec2i aSize)
    {
        vec2i locSRC = new vec2i();
        vec2i locDST = new vec2i();
        for (int j = 0; j < aSize.y; j++)
        {
            locSRC.y = j + aSRCStart.y;
            locDST.y = j + aDSTStart.y;
            for (int i = 0; i < aSize.x; i++)
            {
                locSRC.x = i + aSRCStart.x;
                locDST.x = i + aDSTStart.x;
                
                double datum = aSRC.read(locSRC);
                aDST.write(locDST, datum);
            }
        }
    }
    
    public static void flipAroundHoriz(Image1 aImage)
    {
        int numToFlip = aImage.mSize.y / 2;
        vec2i pixel = new vec2i();
        vec2i other = new vec2i();
        for (int y = 0; y < numToFlip; y++)
        {
            int yOther = aImage.mSize.y - y - 1;
            for (int x = 0; x < aImage.mSize.x; x++)
            {
                pixel.set(x, y);
                other.set(x, yOther);
                int ind0 = aImage.toIntIndex(pixel);
                int ind1 = aImage.toIntIndex(other);
                double tSwap = aImage.mData[ind0];
                aImage.mData[ind0] = aImage.mData[ind1];
                aImage.mData[ind1] = tSwap;
            }
        }
    }
    
    public static void flipAroundVert(Image1 aImage)
    {
        int numToFlip = aImage.mSize.x / 2;
        vec2i pixel = new vec2i();
        vec2i other = new vec2i();
        for (int x = 0; x < numToFlip; x++)
        {
            int xOther = aImage.mSize.x - x - 1;
            for (int y = 0; y < aImage.mSize.y; y++)
            {
                pixel.set(x, y);
                other.set(xOther, y);
                int ind0 = aImage.toIntIndex(pixel);
                int ind1 = aImage.toIntIndex(other);
                double tSwap = aImage.mData[ind0];
                aImage.mData[ind0] = aImage.mData[ind1];
                aImage.mData[ind1] = tSwap;
            }
        }
    }
    
}
